﻿using System;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using Lastfm7.ViewModels;

namespace Lastfm7.Services
{
    public class LastfmService
    {

        #region Singleton

        private static LastfmService instance = new LastfmService();
        public static LastfmService Instance
        {
            get { return instance; }
        }

        private LastfmService() { }

        #endregion

        private const string ApiKey = "a65154a4c8dadfea92f93c0a7c94492f";

        public event EventHandler LoginSuccess;
        public event EventHandler<EventArgs<ServiceException>> Error;

        #region Login

        public void TryLogin(string login, string password)
        {
            Try(() => Login(login, password), "Error logging in");
        }

        void Login(string login, string password)
        {
            if (string.IsNullOrEmpty(login))
                throw new ServiceException("You forgot to enter a login.");

            if (string.IsNullOrEmpty(password))
                throw new ServiceException("You forgot to enter a password.");

            TryCall("user.getinfo", "user={0}".Fmt(login),
                delegate(XDocument doc) {
                    var root = doc.Root;
                    var user = root.Element("user");

                    App.Current.ProfileViewModel = new ProfileViewModel(user);
                    LoginSuccess.Raise(this);
                });
        }

        #endregion

        #region Artist Info

        
        public void TryRetrieveTopArtists()
        {
            TryRetrieveTopArtists(App.Current.ProfileViewModel.Username);
        }

        public void TryRetrieveTopArtists(string user)
        {
            Try(() => RetrieveTopArtists(user), "Couldn't read top artists");
        }

        void RetrieveTopArtists(string user)
        {
            TryCall("user.getTopArtists", "user={0}".Fmt(user),
                delegate(XDocument doc) {
                    var root = doc.Root;
                    var list = root.Element("topartists");

                    App.Current.ProfileViewModel.TopArtistsModel.AddArtists(
                        list.Elements("artist"));
                });
        }

        #endregion

        #region Core

        void TryCall(string method, string query, Action<XDocument> callback)
        {
            Try(() => Call(method, query, callback), "Error talking to last.fm");
        }

        void Call(string method, string query, Action<XDocument> callback)
        {
            try
            {
                var url = string.Format("http://ws.audioscrobbler.com/2.0/?method={0}&{1}&api_key={2}", method, query, ApiKey);
                var client = new WebClient();

                client.DownloadStringCompleted += (s, e)
                    => HandleResponse(e, callback);

                client.DownloadStringAsync(new Uri(url));
            }
            catch (Exception ex)
            {
                Report("Failed to talk to last.fm", ex);
            }
        }

        void HandleResponse(DownloadStringCompletedEventArgs e, Action<XDocument> callback)
        {
            if (e.Error != null)
            {
                string message = "Error talking to last.fm";
                if (e.Error.Message.Contains("NotFound"))
                    message = "User not found, check login";

                Report(message, e.Error);
                return;
            }

            Try(() => callback(XDocument.Parse(e.Result)), "Error parsing last.fm data");
        }

        void Try(Action action, string defaultFailMessage)
        {
            try
            {
                action();
            }
            catch (ServiceException sex)
            {
                Report(sex.Message, null);
            }
            catch (Exception ex)
            {
                Report(defaultFailMessage, ex);
            }
        }

        void Report(string friendlyMessage, Exception inner)
        {
            Error.Raise(this, new EventArgs<ServiceException>(
                new ServiceException(friendlyMessage, inner)));
        }

        #endregion
    }
}
